﻿using System;

namespace CI.Models.Production
{
    /// <summary>
    /// Formulas governing aspects of jobs in EVE. Taken from http://wiki.eve-id.net/Equations.
    /// </summary>
    public class ManufacturingFormula
    {
        /// <summary>
        /// Calculates the time taken to research a BPO to increase it's ME by 1.
        /// </summary>
        /// <param name="baseTime">The base research time of the BPO</param>
        /// <param name="metallurgy">Level of metallurgy skill</param>
        /// <param name="slotMod">Modifier for research slot</param>
        /// <param name="implantMod">Modified for research implants</param>
        /// <returns></returns>
        public static int MaterialEfficienyResearch(int baseTime, int metallurgy, decimal? slotMod, decimal? implantMod)
        {
            return CommonResearch(baseTime, metallurgy, slotMod, implantMod);
        }

        /// <summary>
        /// Calculates the time taken to research a BPO to increase it's PE by 1.
        /// </summary>
        /// <param name="baseTime">The base research time of the BPO</param>
        /// <param name="metallurgy">Level of research skill</param>
        /// <param name="slotMod">Modifier for research slot</param>
        /// <param name="implantMod">Modified for research implants</param>
        /// <returns></returns>
        public static int ProductionEfficienyResearch(int baseTime, int research, decimal? slotMod, decimal? implantMod)
        {
            return CommonResearch(baseTime, research, slotMod, implantMod);
        }

        private static int CommonResearch(int baseTime, int skill, decimal? slotMod, decimal? implantMod)
        {
            decimal timePostSkill = (baseTime) * (1 - (0.05m * skill));
            decimal timePostSlot = slotMod.HasValue ? timePostSkill * slotMod.Value : timePostSkill;
            decimal timePostImplant = implantMod.HasValue ? timePostSlot * implantMod.Value : timePostSlot;

            return (int)Math.Floor(timePostImplant);
        }

        /// <summary>
        /// Calculates the PTM value.
        /// </summary>
        /// <param name="industry">Level of industry skill</param>
        /// <param name="slotMod">Slot modifier</param>
        /// <param name="implantMod">Implant modifier</param>
        /// <returns></returns>
        public static decimal ProductionTimeModifier(int industry, decimal? slotMod, decimal? implantMod)
        {
            decimal postSkill = (1 - (0.04m * industry));
            decimal postSlot = slotMod.HasValue ? postSkill * slotMod.Value : postSkill;
            decimal postImplant = implantMod.HasValue ? postSlot * implantMod.Value : postSlot;

            return postImplant;
        }

        /// <summary>
        /// Calculates the production time
        /// </summary>
        /// <param name="industry">Industry skill level</param>
        /// <param name="slotMod">Slot modifier</param>
        /// <param name="implantMod">Implant modified</param>
        /// <param name="productionEfficiency">BP PE level</param>
        /// <param name="productivityModifier">BP productivity modifier</param>
        /// <param name="baseProductionTime">BP base production time</param>
        /// <returns></returns>
        public static int ProductionTime(int industry, decimal? slotMod, decimal? implantMod, int productionEfficiency,
                                         decimal productivityModifier, int baseProductionTime)
        {
            return
                (int)
                Math.Floor((baseProductionTime *
                            (1m -
                             (productivityModifier / baseProductionTime) *
                             (productionEfficiency / (1m + productionEfficiency))) *
                            ProductionTimeModifier(industry, slotMod, implantMod)));
        }

        public static decimal Waste(decimal materialAmount, decimal baseWasteFactor, int materialEfficiency)
        {
            //

            return materialAmount * (baseWasteFactor / 100) * (1m / (materialEfficiency + 1));
        }

        public static decimal MaterialCost(decimal materialAmount, decimal baseWasteFactor, int materialEfficiency,
                                           int productionEfficiency)
        {
            return
                Round(materialAmount + (materialAmount * (0.25m - (0.05m * productionEfficiency))) +
                      (materialAmount * (baseWasteFactor / 100) * (1m / (materialEfficiency + 1))));
        }

        /// <summary>
        /// Rounds to the nearest integer.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static decimal Round(decimal value)
        {
            return Math.Round(value);
        }

        private static decimal Ceiling(decimal value)
        {
            return Math.Ceiling(value);
        }
    }
}